// This is the main DLL file.


#include "rtgen_worker.h"
#include <vcclr.h>
//#include <sstream>
#include <string>
#include <math.h>
#include "ChainWalkSet.h"
#include "ChainWalkContext.h"
#include "Public.h"
#include "md5.h"

rtgen_worker::RainbowCrack::RainbowCrack()
{
//	pGenerator = new CRainbowTableGenerator();
	m_pCracker = new CDictionaryCracker();
}
rtgen_worker::RainbowCrack::~RainbowCrack()
{
	delete m_pCracker;
//	pGenerator = NULL;
}
/*
Int32 rtgen_worker::RainbowCrack::GetKeyRate()
{
	return nKeyRate;
}*/



int QuickSortPartition(RainbowChain* pChain, int nLow, int nHigh)
{
	int nRandomIndex = nLow + ((unsigned int)rand() * (RAND_MAX + 1) + (unsigned int)rand()) % (nHigh - nLow + 1);
	RainbowChain TempChain;
	TempChain = pChain[nLow];
	pChain[nLow] = pChain[nRandomIndex];
	pChain[nRandomIndex] = TempChain;

	TempChain = pChain[nLow];
	uint64 nPivotKey = pChain[nLow].nIndexE;
	while (nLow < nHigh)
	{
		while (nLow < nHigh && pChain[nHigh].nIndexE >= nPivotKey)
			nHigh--;
		pChain[nLow] = pChain[nHigh];
		while (nLow < nHigh && pChain[nLow].nIndexE <= nPivotKey)
			nLow++;
		pChain[nHigh] = pChain[nLow];
	}
	pChain[nLow] = TempChain;
	return nLow;
}

void QuickSort(RainbowChain* pChain, int nLow, int nHigh)
{
	if (nLow < nHigh)
	{
		int nPivotLoc = QuickSortPartition(pChain, nLow, nHigh);
		QuickSort(pChain, nLow, nPivotLoc - 1);
		QuickSort(pChain, nPivotLoc + 1, nHigh);
	}
}

void loadHashList(array<String^> ^HashList, unsigned char **ppHashList, int &nSize)
{
	// Clean up the old content
	if(nSize > 0 && ppHashList != NULL)
	{
		for(int i = 0; i < nSize; i++)
		{
			delete ppHashList[i];
		}
		delete [] ppHashList;
	}
	ppHashList = new unsigned char*[HashList->Length];
	for(int i = 0; i < HashList->Length; i++)
	{
		unsigned char *pHash = new unsigned char[16];
		int nLen = 16;
		string sHash = (char*)(void*)Marshal::StringToHGlobalAnsi(HashList[i]);
		ParseHash(sHash, pHash, nLen);
		ppHashList[i] = pHash;		
	}
	nSize = HashList->Length;
	//return ppHashList;
}
/*
inline int checkPass(char *word, int wordlen, HASHROUTINE pHashFunction, PasswordCrackedCallback ^cbPassCracked)
{
	unsigned char hash[33] = {0};
	return 0;
}*/

void PassCracked(char *pHash, string sPlain)
{
//	Console::Write(sPlain.c_str());
	rtgen_worker::RainbowCrack::ReportCracked(gcnew String(pHash), gcnew String(sPlain.c_str()));
}


int rtgen_worker::RainbowCrack::DoChainCheck(String ^Hash, String ^HashRoutine, String ^CharsetName, System::UInt32 PlainLenMin, System::UInt32 PlainLenMax, System::UInt32 RainbowTableIndex, System::UInt32 RainbowChainLen, System::Collections::Generic::Queue<RainbowCrackChain^> ^qChains, String ^Password)
{
	std::string sHashRoutineName = (char*)(void*)Marshal::StringToHGlobalAnsi(HashRoutine);
	std::string sPlainCharsetName = (char*)(void*)Marshal::StringToHGlobalAnsi(CharsetName);
	Hash = Hash->ToLower(); // Convert to lowercase
	std::string sHash = (char*)(void*)Marshal::StringToHGlobalAnsi(Hash);
	CChainWalkContext cwc;
	if (!cwc.SetHashRoutine(sHashRoutineName))
	{
//		cb(Hash, Tableset, "hash routine not supported\n");
		return 1;
	}
//	char *pCharset;	
//	GetCharset(sPlainCharsetName, &pCharset);

	if (!cwc.SetPlainCharset(sPlainCharsetName, PlainLenMin, PlainLenMax))
	{
//		cb(Hash, Tableset, "Invalid charset");
		return 2;
	}
	if (!cwc.SetRainbowTableIndex(RainbowTableIndex))
	{
//		cb(Hash, Tableset, "invalid rainbow table index");
		return 3;
	}
	

//		ParseHash(sHash, TargetHash, nHashLen);

	CChainWalkSet m_cws;
	while(qChains->Count > 0)
	{
		
		RainbowCrackChain ^pChain = (RainbowCrackChain^)qChains->Dequeue();
		cwc.SetIndex(pChain->nStartingPoint);	
		int nPos;
		for (nPos = 0; nPos < pChain->nGuessedPos; nPos++)
		{
			cwc.IndexToPlain();
			cwc.PlainToHash();
			cwc.HashToIndex(nPos);
		}
		cwc.IndexToPlain();
		cwc.PlainToHash();
		std::string sHashme = cwc.GetHash();
		if(sHashme.compare(sHash) == 0)
		{
			Password = gcnew String(cwc.GetPlain().c_str());
			return 0;
		}
	}
	return 0;
}
int rtgen_worker::RainbowCrack::CalculateIndexList(array<UInt64> ^Indices, String ^Hash, String ^HashRoutineName, String ^PlainCharsetName, System::UInt32 PlainLenMin, System::UInt32 PlainLenMax, System::UInt32 RainbowTableIndex, System::UInt32 RainbowChainLen, System::UInt32 RequestID, UpdateCrackingProgressCallback ^cb)
{
	unsigned char TargetHash[MAX_HASH_LEN];
	String ^Tableset = PlainCharsetName + " " + PlainLenMin + "-" + PlainLenMax + " " + RainbowChainLen + " " + RainbowTableIndex;

	int nHashLen;
	std::string sHashRoutineName = (char*)(void*)Marshal::StringToHGlobalAnsi(HashRoutineName);
	std::string sPlainCharsetName = (char*)(void*)Marshal::StringToHGlobalAnsi(PlainCharsetName);
	std::string sHash = (char*)(void*)Marshal::StringToHGlobalAnsi(Hash);
	CChainWalkContext cwc;
	if (!cwc.SetHashRoutine(sHashRoutineName))
	{
		//cb(Hash, Tableset, "hash routine not supported\n");
		return 1;
	}

	if (!cwc.SetPlainCharset(sPlainCharsetName, PlainLenMin, PlainLenMax))
	{
		//cb(Hash, Tableset, "Invalid charset");
		return 2;
	}
	if (!cwc.SetRainbowTableIndex(RainbowTableIndex))
	{
		//cb(Hash, Tableset, "invalid rainbow table index");
		return 3;
	}

		ParseHash(sHash, TargetHash, nHashLen);
		CChainWalkSet m_cws;
		//if (nHashLen != CChainWalkContext::GetHashLen())
		//	printf("debug: nHashLen mismatch\n");

		// Rqeuest ChainWalk
		bool fNewlyGenerated;
//		printf("Requesting walk of '%s'", TargetHash);
		uint64* pStartPosIndexE = m_cws.RequestWalk(TargetHash,
													nHashLen,
													sHashRoutineName,
													sPlainCharsetName,
													PlainLenMin,
													PlainLenMax,
													RainbowTableIndex,
													RainbowChainLen,
													fNewlyGenerated);
//		printf("done!\n");
/*		printf("debug: using %s walk for %s\n", fNewlyGenerated ? "newly generated" : "existing",
												sHash);
*/		// Walk
		int nPos;
		float nTargetChains = pow((float)RainbowChainLen - 2, 2) / 2 - (RainbowChainLen - 2);
		if (fNewlyGenerated)
		{
			time_t tStart = 0;
//			printf("Pregenerating index...");
			for (nPos = RainbowChainLen - 2; nPos >= 0; nPos--)
			{
				if(time(NULL) - tStart > 1)
				{
					time(&tStart);
					float nCurrentChains = pow(((float)RainbowChainLen - 2 - (float)nPos), 2) / 2 - (RainbowChainLen - 2);
					float fResult = ((float)((float)(nCurrentChains) / (float)nTargetChains) * 100);
					if(fResult < 0)
						fResult = 0;
					String ^sProgress = String::Format("{0:##}%", fResult);
					//cb(Hash, Tableset, sProgress);
					cb(RequestID, fResult);
				}
				
				cwc.SetHash(TargetHash);
				cwc.HashToIndex(nPos);
				int i;
				for (i = nPos + 1; i <= RainbowChainLen - 2; i++)
				{
					cwc.IndexToPlain();
					cwc.PlainToHash();
					cwc.HashToIndex(i);
				}
				Indices[nPos] = cwc.GetIndex();
					//nChainWalkStep += nRainbowChainLen - 2 - nPos;
			}
//			printf("ok\n");			
			float nCurrentChains = pow(((float)RainbowChainLen - 2 - (float)nPos), 2) / 2 - (RainbowChainLen - 2);
			float fResult = ((float)((float)(nCurrentChains) / (float)nTargetChains) * 100);
			String ^sProgress = String::Format("{0:##}%", fResult);
			//cb(Hash, Tableset, sProgress);
			cb(RequestID, fResult);

			return 0;
		}
	return 0;
}

int rtgen_worker::RainbowCrack::SortFile(String ^Filename)
{
	std::string sPathName = (char*)(void*)Marshal::StringToHGlobalAnsi(Filename);
	FILE* file = fopen(sPathName.c_str(), "r+b");
	if (file == NULL)
	{
		printf("failed to open %s\n", sPathName.c_str());
		return 1;
	}

	// Sort
	unsigned int nFileLen = GetFileLen(file);
	if (nFileLen % 16 != 0)
		return 2; // printf("rainbow table size check fail\n");
		
	int nRainbowChainCount = nFileLen / 16;
	RainbowChain* pChain = (RainbowChain*)new unsigned char[nFileLen];
	if (pChain != NULL)
	{
		// Load file
//		printf("loading rainbow table...\n");
		fseek(file, 0, SEEK_SET);
		if (fread(pChain, 1, nFileLen, file) != nFileLen)
		{
//			printf("disk read fail\n");
			return 3;
		}

		// Sort file
//		printf("sorting rainbow table...\n");
		QuickSort(pChain, 0, nRainbowChainCount - 1);

		// Write file
//		printf("writing sorted rainbow table...\n");
		fseek(file, 0, SEEK_SET);
		fwrite(pChain, 1, nFileLen, file);
		fclose(file);
		delete[] pChain;
		return 0;
	}
	else
		printf("memory allocation fail\n");
	return 4;
}
/*
int rtgen_worker::RainbowCrack::calculateTable(String ^fileName, String ^HashRoutineName, String ^CharsetName, System::Int32 nPlainLenMin, System::Int32 nPlainLenMax, System::Int32 nRainbowTableIndex, System::Int32 nRainbowChainLen, System::Int32 nRainbowChainCount, String ^Salt)
{
	std::string sHashRoutineName = (char*)(void*)Marshal::StringToHGlobalAnsi(HashRoutineName);
	std::string sCharsetName = (char*)(void*)Marshal::StringToHGlobalAnsi(CharsetName);
	std::string sFilename = (char*)(void*)Marshal::StringToHGlobalAnsi(fileName);
	std::string sSalt;
	if(Salt->IsNullOrEmpty(Salt) == false)
		sSalt = (char*)(void*)Marshal::StringToHGlobalAnsi(Salt);
	return pGenerator->CalculateTable(sFilename, nRainbowChainCount, sHashRoutineName, sCharsetName, nPlainLenMin, nPlainLenMax, nRainbowTableIndex, nRainbowChainLen, sSalt);

}*/
/*
int rtgen_worker::RainbowCrack::GetSpeed()
{
	if(pGenerator != NULL)
		return pGenerator->GetCalculationSpeed();
	return 0;
}
*/
/*
int rtgen_worker::RainbowCrack::GetCurrentCalculatedChains()
{
	if(pGenerator != NULL)
		return pGenerator->GetCurrentCalculatedChains();
	return 0;
}*/